package com.utils

import org.apache.commons.io.FilenameUtils
import org.apache.commons.io.IOUtils
import org.apache.tools.zip.ZipEntry
import org.apache.tools.zip.ZipFile
import org.apache.tools.zip.ZipOutputStream
import java.io.*
import java.util.*

/**
 * 压缩工具类
 */
object ZipUtils {
    private const val DEFAULT_CHARSET = "UTF-8"

    /**
     * 压缩文件夹
     * @param zipFileName 打包后文件的名称，含路径
     * @param zipPathName 打包目的文件夹名,为空则表示直接打包到根
     * @param sourceFolder 需要打包的文件夹或者文件的路径
     */
    fun zip(zipFileName: String, zipPathName: String, vararg sourceFolder: String?) {
        var zipPathName = zipPathName
        var out: ZipOutputStream? = null
        println("zipfilename=$zipFileName, zipPathName=$zipPathName, so=$sourceFolder")

        val zipFile = File(zipFileName)
        zipFile.parentFile.mkdirs()
        out = ZipOutputStream(zipFile)
        out.use {
            it.encoding = DEFAULT_CHARSET
            zipPathName = if (zipPathName.isNotBlank()) {
                FilenameUtils.normalizeNoEndSeparator(zipPathName, true) + "/"
            } else {
                ""
            }
            for (i in sourceFolder.indices) {
                zip(it, sourceFolder[i], zipPathName)
            }
        }
    }

    @Throws(Exception::class)
    fun zipFileList(zipFile: String, source: String?, isSaveFileName: Boolean) {
        val file = File(source)
        val files = file.listFiles()
        val strings = files.map { obj -> obj.path }.toTypedArray()
        zip(zipFile, if (isSaveFileName) file.name else "", *strings)
    }

    /**
     * 压缩文件夹
     * @param zipFile [java.lang.String] object. 打包后文件的名称，含路径
     * @param source [java.lang.String] object. 需要打包的文件夹或者文件的路径
     */
    @Throws(Exception::class)
    fun zip(zipFile: String, source: String?) {
        val file = File(source)
        zip(zipFile, if (file.isFile) "" else file.name, source)
    }

    /**
     * 压缩文件夹
     * @param zipFile [java.io.File] object. 打包后文件的名称，含路径
     * @param source [java.io.File] object. 需要打包的文件夹或者文件的路径
     */
    @Throws(Exception::class)
    fun zip(zipFile: File, source: File) {
        zip(zipFile.absolutePath, source.absolutePath)
    }

    @Throws(IOException::class)
    private fun zip(zos: ZipOutputStream, file: String?, pathName: String) {
        var pathName = pathName
        val file2zip = File(file)
        if (file2zip.isFile) {
            zos.putNextEntry(ZipEntry(pathName + file2zip.name))
            val inputStream = FileInputStream(file2zip.absolutePath)
            IOUtils.copy(inputStream, zos)

            inputStream.close()
            zos.flush()
            zos.closeEntry()
        } else {
            val files = file2zip.listFiles()
            pathName += file2zip.name + "/"
            if (files!!.isNotEmpty()) {
                for (f in files) {
                    if (f.isDirectory) {
                        zip(
                            zos,
                            FilenameUtils.normalizeNoEndSeparator(f.absolutePath, true),
                            FilenameUtils.normalizeNoEndSeparator(pathName, true)
                                    + "/"
                        )
                    } else {
                        zos.putNextEntry(ZipEntry(pathName + f.name))
                        val inputStream = FileInputStream(f.absolutePath)
                        IOUtils.copy(inputStream, zos)
                        inputStream.close()
                        zos.flush()
                        zos.closeEntry()
                    }
                }
            }
        }
    }

    /**
     * 解压
     *
     * @param fromZipFile
     * zip文件路径
     * @param unzipPath
     * 解压路径
     */
    @Throws(Exception::class)
    fun unzip(fromZipFile: String?, unzipPath: String) {
        var fos: FileOutputStream? = null
        var `is`: InputStream? = null
        var path1 = ""
        var tempPath = ""

        if (!File(unzipPath).exists()) {
            File(unzipPath).mkdir()
        }
        val zipFile = ZipFile(fromZipFile, DEFAULT_CHARSET)
        val temp = File(unzipPath)
        val strPath = temp.absolutePath
        val enu = zipFile.entries
        var zipEntry: ZipEntry? = null
        while (enu.hasMoreElements()) {
            zipEntry = enu.nextElement() as ZipEntry
            path1 = zipEntry.name
            if (zipEntry.isDirectory) {
                tempPath = FilenameUtils.normalizeNoEndSeparator(strPath + File.separator + path1, true)
                val dir = File(tempPath)
                dir.mkdirs()
                continue
            } else {
                var bis: BufferedInputStream? = null
                var bos: BufferedOutputStream? = null
                try {
                    `is` = zipFile.getInputStream(zipEntry)
                    bis = BufferedInputStream(`is`)
                    path1 = zipEntry.name
                    tempPath = FilenameUtils.normalizeNoEndSeparator(strPath + File.separator + path1, true)
                    File(tempPath).parentFile.mkdirs()
                    fos = FileOutputStream(tempPath)
                    bos = BufferedOutputStream(fos)

                    IOUtils.copy(bis, bos)
                } finally {
                    IOUtils.closeQuietly(bis)
                    IOUtils.closeQuietly(bos)
                    IOUtils.closeQuietly(`is`)
                    IOUtils.closeQuietly(fos)
                }
            }
        }
    }
}